package net.ib.mtalk.ui.attachment.vcard;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;

/**
 * vCard 포맷 형태로 만든다.
 * 
 * @author happy2v
 * @
 * @version 0.2.2
 */
public class VCardComposer {
	
	// 버전을 나타내는 상수.
	public final static int VERSION_VCARD21_INT = 1;
	public final static int VERSION_VCARD30_INT = 2;
	public final static int VERSION_VCARD40_INT = 3;

	// 메소드 타입?
	public static final int METHOD_TYPE_BDAY 		= 8;
	public static final int METHOD_TYPE_ANNIVERSARY = 9;
	public static final int METHOD_TYPE_WEBSITE 	= 10;
	public static final int METHOD_TYPE_OTHER 		= 11;
	
	/**
	 *  메소드별 타입 맵
	 */
	private static final HashMap<Integer, String> phoneTypeMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> emailTypeMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> postalTypeMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> imTypeMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> imProtocolMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> organizationTypeMap = new HashMap<Integer, String>();
	private static final HashMap<Integer, String> methodTypeMap = new HashMap<Integer, String>();
	
	static{
		phoneTypeMap.put(Phone.TYPE_HOME, "HOME");
        phoneTypeMap.put(Phone.TYPE_MOBILE, "CELL");
        phoneTypeMap.put(Phone.TYPE_WORK, "WORK");
        phoneTypeMap.put(Phone.TYPE_FAX_WORK, "WORK-FAX");
        phoneTypeMap.put(Phone.TYPE_FAX_HOME, "HOME-FAX");
        phoneTypeMap.put(Phone.TYPE_PAGER, "PAGER");
        phoneTypeMap.put(Phone.TYPE_OTHER, "OTHER");
        phoneTypeMap.put(Phone.TYPE_CALLBACK, "CALLBACK");
        phoneTypeMap.put(Phone.TYPE_CAR, "CAR");
        phoneTypeMap.put(Phone.TYPE_COMPANY_MAIN, "COMPANY_MAIN");
        phoneTypeMap.put(Phone.TYPE_ISDN, "ISDN");
        phoneTypeMap.put(Phone.TYPE_MAIN, "MAIN");
        phoneTypeMap.put(Phone.TYPE_OTHER_FAX, "OTHER_FAX");
        phoneTypeMap.put(Phone.TYPE_RADIO, "RADIO");
        phoneTypeMap.put(Phone.TYPE_TELEX, "TELEX");
        phoneTypeMap.put(Phone.TYPE_TTY_TDD, "TTY_TDD");
        phoneTypeMap.put(Phone.TYPE_WORK_MOBILE, "WORK_MOBILE");
        phoneTypeMap.put(Phone.TYPE_WORK_PAGER, "WORK_PAGER");
        phoneTypeMap.put(Phone.TYPE_ASSISTANT, "ASSISTANT");
        phoneTypeMap.put(Phone.TYPE_MMS, "MMS");
        
        emailTypeMap.put(Email.TYPE_HOME, "HOME");
        emailTypeMap.put(Email.TYPE_WORK, "WORK");
        emailTypeMap.put(Email.TYPE_OTHER, "OTHER");
        emailTypeMap.put(Email.TYPE_MOBILE, "MOBILE");
        
        postalTypeMap.put(StructuredPostal.TYPE_HOME, "HOME");
        postalTypeMap.put(StructuredPostal.TYPE_WORK, "WORK");
        postalTypeMap.put(StructuredPostal.TYPE_OTHER, "OTHER");
        
        imTypeMap.put(Im.TYPE_HOME, "HOME");		
        imTypeMap.put(Im.TYPE_OTHER, "OTHER");		
        imTypeMap.put(Im.TYPE_WORK, "WORK");		
        
        imProtocolMap.put(Im.PROTOCOL_AIM, "AIM");
        imProtocolMap.put(Im.PROTOCOL_GOOGLE_TALK, "GOOGLE_TALK");
        imProtocolMap.put(Im.PROTOCOL_ICQ, "ICQ");
        imProtocolMap.put(Im.PROTOCOL_JABBER, "JABBER");
        imProtocolMap.put(Im.PROTOCOL_MSN, "MSN");
        imProtocolMap.put(Im.PROTOCOL_NETMEETING, "NETMEETING");
        imProtocolMap.put(Im.PROTOCOL_QQ, "QQ");
        imProtocolMap.put(Im.PROTOCOL_SKYPE, "SKYPE");
        imProtocolMap.put(Im.PROTOCOL_YAHOO, "YAHOO");
        
        organizationTypeMap.put(Organization.TYPE_OTHER, "OTHER");
        organizationTypeMap.put(Organization.TYPE_WORK, "WORK");
        
        methodTypeMap.put(METHOD_TYPE_OTHER, "OTHER");
        methodTypeMap.put(METHOD_TYPE_BDAY, "BDAY");
        methodTypeMap.put(METHOD_TYPE_ANNIVERSARY, "ANNIVERSARY");
        methodTypeMap.put(METHOD_TYPE_WEBSITE, "WEBSITE");
	}
	
	/**
	 * A New Line
	 */
	private String mNewline;
	
	/**
	 * The Composed String
	 */
	private StringBuilder mResult;
	
	/**
	 * vCard 문자열을 만든다.
	 * @param struct
	 * @param vcardVersion
	 * @return
	 * @throws Exception
	 */
	public String createVCard(ContactStruct struct, int vcardVersion)
			throws Exception{
		mResult = new StringBuilder();
		
		if (vcardVersion == VERSION_VCARD21_INT) {
            mNewline = "\r\n";
        } else if (vcardVersion == VERSION_VCARD30_INT) {
            mNewline = "\n";
        } else {
        	mNewline = "\n";
        }
		
		/**
		 * VCARD 시작
		 */
		mResult.append("BEGIN:VCARD").append(mNewline);
		
		// 버젼 정보 입력
        // VERSION:
        if (vcardVersion == VERSION_VCARD21_INT) {
            mResult.append("VERSION:2.1").append(mNewline);
        } else if (vcardVersion == VERSION_VCARD30_INT){
            mResult.append("VERSION:3.0").append(mNewline);
        } else if (vcardVersion == VERSION_VCARD40_INT){
       	 	mResult.append("VERSION:4.0").append(mNewline);
        } else{
       	 	mResult.append("VERSION:OTHER").append(mNewline);
        }
        
		// name이 null 이고 fullName 이 null 이 아니면
		if(isNull(struct.name)){
			if(!isNull(struct.fullName)){
				struct.name = struct.fullName;
				mResult.append("N:").append(struct.fullName).append(mNewline);
	       		mResult.append("FN:").append(struct.fullName).append(mNewline);
			}else{
				// 둘다 null 이면
				struct.name = struct.fullName = "Name";
				mResult.append("N:").append(struct.fullName).append(mNewline);
	       		mResult.append("FN:").append(struct.fullName).append(mNewline);
			}
		}else{
			if(!isNull(struct.fullName)){
				if(struct.name.equals(struct.fullName)){
					mResult.append("N:").append(struct.fullName).append(mNewline);
		       		mResult.append("FN:").append(struct.fullName).append(mNewline);
				}else{
					mResult.append("N:").append(struct.name).append(mNewline);
		       		mResult.append("FN:").append(struct.fullName).append(mNewline);
				}
			}else{
				mResult.append("N:").append(struct.name).append(mNewline);
	       		mResult.append("FN:").append(struct.name).append(mNewline);
			}
		}
		
		// ORG:
        if (!isNull(struct.company)) {
            mResult.append("ORG:").append(struct.company).append(mNewline);
        }
        
		// TITLE
		if(!isNull(struct.title)){
			mResult.append("TITLE:")
			.append(foldingString(struct.title, vcardVersion))
			.append(mNewline);
		}
		
		// TEL : 전화번호
        if (struct.phoneList != null) {
            appendPhoneStr(struct.phoneList, vcardVersion);
        }
        
        // EMAIL : 이메일
        if (struct.emailList != null) {
            appendEmailStr(struct.emailList, vcardVersion);
        }
        
        // ADR :	주소
        if (struct.postalList != null) {
            appendPostalStr(struct.postalList, vcardVersion);
        }
        
        // IMPP : 메신져 
        if (struct.imList != null) {
       	 	appendImStr(struct.imList, vcardVersion);
        }
        
        // ORG : 조직도
        if (struct.organizationList != null){
        	appendOrganizationStr(struct.organizationList, vcardVersion);
        }
        
        // NOTE : 메모
        if (struct.noteList != null){
        	appendNoteStr(struct.noteList, vcardVersion);
        }
        
        // 기타 메소드
        if (struct.contactmethodList != null) {
            appendContactMethodStr(struct.contactmethodList, vcardVersion);
        }
        
        // END:VCARD 마지막을 알린다.
        mResult.append("END:VCARD").append(mNewline);
		return mResult.toString();
	}
	
	/**
     * 버젼에 따라 개행 문자를 지정한다.
     * @param str  문자열
     * @param version  버젼
     * @return the folded string
     */
    private String foldingString(String str, int version) {
        if (str.endsWith("\r\n")) {
            str = str.substring(0, str.length() - 2);
        } else if (str.endsWith("\n")) {
            str = str.substring(0, str.length() - 1);
        } else {
            return null;
        }

        str = str.replaceAll("\r\n", "\n");
        if (version == VERSION_VCARD21_INT) {
            return str.replaceAll("\n", "\r\n ");
        } else if (version == VERSION_VCARD30_INT) {
            return str.replaceAll("\n", "\n ");
        } else {
            return null;
        }
    }
	
	private boolean isNull(String str) {
        if (str == null || str.trim().equals("")) {
            return true;
        }
        return false;
    }
	
	/** vCard 전화번호를 만들어준다. */
    private void appendPhoneStr(List<ContactStruct.PhoneData> phoneList, int version) {
        HashMap<String, String> numMap = new HashMap<String, String>();
        String joinMark = ((version == VERSION_VCARD21_INT) ? ";" : ",");

        // 번호와 타입을 정렬한다. 하나의 번호에 여러타입이 있을 경우 하나로 묶는다.
        for (ContactStruct.PhoneData phone : phoneList) {
            String type;
            if (!isNull(phone.number) && (phone.isPrimary == true)) {
                type = getPhoneTypeStr(phone);
            	if(version == VERSION_VCARD30_INT && type.indexOf(";") != -1){
            		type = type.replace(";", ",");
            	}
            	// Map에 number가 있으면 타입을 추가한다.
            	if(numMap.containsKey(phone.number)){
            		type = numMap.get(phone.number) + joinMark + type;
            	}
            	// <번호와 타입> 을 해쉬맵에 넣는다.
            	numMap.put(phone.number, type);
            }
        }
        // 맵에 저장된 데이터를 vCard 형태로 타나내어준다.
        for(Map.Entry<String, String> num : numMap.entrySet()){
        	if (version == VERSION_VCARD21_INT) {
                mResult.append("TEL;");
            } else { // vcard3.0
                mResult.append("TEL;TYPE=");
            }
            mResult.append(num.getValue()).append(":").append(num.getKey())
                    .append(mNewline);
        }
    }
    
    /** 전화번호의 타입을 만들어준다. */
    private String getPhoneTypeStr(ContactStruct.PhoneData phone) {
        int phoneType = phone.type;
        String typeStr, label;

        if (phoneTypeMap.containsKey(phoneType)) {
            typeStr = phoneTypeMap.get(phoneType);
        } else if (phoneType == Phone.TYPE_CUSTOM) {
        	if(!isNull(phone.label)){
        		label = phone.label.toUpperCase();        		
                    typeStr = "CUSTOM-" + label;
            }else{
            	typeStr = "X-CUSTOM";
            }
        } else {
        	typeStr = "X-CUSTOM";
        }
        return typeStr;
    }
    
    /** vCard 이메일을 만들어준다. */
    private void appendEmailStr(List<ContactStruct.EmailData> emailList, int version) {
        HashMap<String, String> emailMap = new HashMap<String, String>();
        String joinMark = ((version == VERSION_VCARD21_INT) ? ";" : ",");

        // 이메일의 주소와 타입을 정렬한다. 하나의 이메일에 여러타입이 있을 경우 하나로 묶는다.
        for (ContactStruct.EmailData email : emailList) {
            String type;
            if (!isNull(email.address) && (email.isPrimary == true)) {
                type = getEmailTypeStr(email);
            	if(version == VERSION_VCARD30_INT && type.indexOf(";") != -1){
            		type = type.replace(";", ",");
            	}
            	// Map에 address가 있으면 타입을 추가한다.
            	if(emailMap.containsKey(email.address)){
            		type = emailMap.get(email.address) + joinMark + type;
            	}
            	// <번호와 타입> 을 해쉬맵에 넣는다.
            	emailMap.put(email.address, type);
            }
        }
        // 맵에 저장된 데이터를 vCard 형태로 타나내어준다.
        for(Map.Entry<String, String> address : emailMap.entrySet()){
        	if (version == VERSION_VCARD21_INT) {
                mResult.append("EMAIL;");
            } else { // vcard3.0
                mResult.append("EMAIL;TYPE=");
            }
            mResult.append(address.getValue()).append(":").append(address.getKey())
                    .append(mNewline);
        }
    }
    
    /** 이메일의 타입을 만들어준다. */
    private String getEmailTypeStr(ContactStruct.EmailData email) {
        int emailType = email.type;
        String typeStr, label;

        if (emailTypeMap.containsKey(emailType)) {
            typeStr = emailTypeMap.get(emailType);
        } else if (emailType == Email.TYPE_CUSTOM) {
        	if(!isNull(email.label)){
        		label = email.label.toUpperCase();        		
                    typeStr = "CUSTOM-" + label;
            }else{
            	typeStr = "X-CUSTOM";
            }
        } else {
        	typeStr = "X-CUSTOM";
        }
        return typeStr;
    }
    
    /** vCard 주소를 만들어준다. */
    private void appendPostalStr(List<ContactStruct.PostalData> postalList, int version) {
        for (ContactStruct.PostalData postal : postalList) {
            String type;
            // 포맷화된 주소가 있을때 각 요소를 추츨해서 vCard 주소 형식으로 만들어준다.
            if (!isNull(postal.formattedAddress) && (postal.isPrimary == true)) {
                type = getPostalTypeStr(postal);
            	if(version == VERSION_VCARD30_INT && type.indexOf(";") != -1){
            		type = type.replace(";", ",");
            	}
            	
            	if (version == VERSION_VCARD21_INT) {
                    mResult.append("ADR;");
                } else { // vcard3.0
                    mResult.append("ADR;TYPE=");
                }
            	
            	mResult.append(type).append(":");
            	
            	if(!isNull(postal.street)){
                	mResult.append(postal.street).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.pobox)){
                	mResult.append(postal.pobox).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.neighborhood)){
                	mResult.append(postal.neighborhood).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.city)){
                	mResult.append(postal.city).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.region)){
                	mResult.append(postal.region).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.postalCode)){
                	mResult.append(postal.postalCode).append(";");
                }else{
                	mResult.append(";");
                }
                if(!isNull(postal.country)){
                	mResult.append(postal.country).append(mNewline);
                }else{
                	mResult.append(mNewline);
                }
            }
        }
    }
    
    /** 주소의 타입을 만들어준다. */
    private String getPostalTypeStr(ContactStruct.PostalData postal) {
        int postalType = postal.type;
        String typeStr, label;

        if (postalTypeMap.containsKey(postalType)) {
            typeStr = postalTypeMap.get(postalType);
        } else if (postalType == Phone.TYPE_CUSTOM) {
        	if(!isNull(postal.label)){
        		label = postal.label.toUpperCase();        		
                    typeStr = "CUSTOM-" + label;
            }else{
            	typeStr = "X-CUSTOM";
            }
        } else {
        	typeStr = "X-CUSTOM";
        }
        return typeStr;
    }
    
    /** vCard 메신져를 만들어준다. */
    private void appendImStr(List<ContactStruct.ImData> imList, int version) {
        HashMap<String, String> imMap = new HashMap<String, String>();
        String joinMark = ((version == VERSION_VCARD21_INT) ? ";" : ",");

        // 메신져 주소와 프로토콜을 정렬한다. 하나의 번호에 여러프로토콜이 있을 경우 하나로 묶는다.
        for (ContactStruct.ImData im : imList) {
            String protocol;
            if (!isNull(im.data) && (im.isPrimary == true)) {
            	protocol = getImProtocolStr(im);
            	if(version == VERSION_VCARD30_INT && protocol.indexOf(";") != -1){
            		protocol = protocol.replace(";", ",");
            	}
            	// Map에 im.data가 있으면 프로토콜을 추가한다.
            	if(imMap.containsKey(im.data)){
            		protocol = imMap.get(im.data) + joinMark + protocol;
            	}
            	// <메신져주소와 프로토콜> 을 해쉬맵에 넣는다.
            	imMap.put(im.data, protocol);
            }
        }
        // 맵에 저장된 데이터를 vCard 형태로 타나내어준다.
        for(Map.Entry<String, String> im : imMap.entrySet()){
        	if (version == VERSION_VCARD21_INT) {
                mResult.append("IMPP;");
            } else { // vcard3.0
                mResult.append("IMPP;TYPE=");
            }
            mResult.append(im.getValue()).append(":").append(im.getKey())
                    .append(mNewline);
        }
    }
    
    /** 메신져의 타입을 만들어준다. */
    // 타입대신 프로토콜을 사용한다.
    private String getImTypeStr(ContactStruct.ImData im) {
        int imType = im.type;
        String typeStr, label;

        if (imTypeMap.containsKey(imType)) {
            typeStr = imTypeMap.get(imType);
        } else if (imType == Im.TYPE_CUSTOM) {
        	if(!isNull(im.label)){
        		label = im.label.toUpperCase();        		
                    typeStr = "CUSTOM-" + label;
            }else{
            	typeStr = "X-CUSTOM";
            }
        } else {
        	typeStr = "X-CUSTOM";
        }
        return typeStr;
    }
    
    /** 메신져의 프로토콜을 만들어준다. */
    private String getImProtocolStr(ContactStruct.ImData im) {
        int imProtocol = im.protocol;
        String protocolStr, protocolLabel;

        if (imProtocolMap.containsKey(imProtocol)) {
        	protocolStr = imProtocolMap.get(imProtocol);
        } else if (imProtocol == Im.PROTOCOL_CUSTOM) {
        	if(!isNull(im.customProtocol)){
        		protocolLabel = im.customProtocol.toUpperCase();        		
        		protocolStr = "CUSTOM-" + protocolLabel;
            }else{
            	protocolStr = "X-CUSTOM";
            }
        } else {
        	protocolStr = "X-CUSTOM";
        }
        return protocolStr;
    }
    
    /** vCard 조직도를 만들어준다. */
    private void appendOrganizationStr(List<ContactStruct.OrganizationData> organizationList, int version) {
        for (ContactStruct.OrganizationData organization : organizationList) {
            if (!isNull(organization.company) && (organization.isPrimary == true)) {
            	mResult.append("ORG:");
            	mResult.append(organization.company).append(mNewline);
            }
        }
    }
    
    /** vCard 메모를 만들어준다. */
    private void appendNoteStr(List<ContactStruct.NoteData> noteList, int version) {
        for (ContactStruct.NoteData note : noteList) {
            if (!isNull(note.note) && (note.isPrimary == true)) {
                mResult.append("NOTE:");
                mResult.append(note.note).append(mNewline);
            }
        }
    }    
    
    /** vCard 메소드를 만들어준다. */
    private void appendContactMethodStr(List<ContactStruct.ContactMethod> contactMList, int version) {
    }
}
